// import assert from 'nanoassert';

/**
 * Wire type decoders mapping the `type` integer to the basic decoder.
 * Groups are not supported, and the higher-level types are done in a 2nd pass
 * @type {Object}
 */
export const decoders = {
	0: varint,
	1: fixed64bit,
	2: bytes,
	5: fixed32bit,
};

export function tag(buf, byteOffset = 0) {
	const [n, bytes] = varint(buf, byteOffset);
	const int = Number(n); // Safe as protoc only allows fieldNumber up to i32 + 3 bits for wireType
	const wireType = int & 0b111;
	const fieldNumber = int >> 3;

	return [{ wireType, fieldNumber }, bytes];
}

export function varint(buf, byteOffset = 0) {
	let o = byteOffset;
	let acc = 0n;
	let shift = 0n;
	while (buf[o] & 0b1000_0000) {
		acc |= BigInt(buf[o++] & 0b0111_1111) << shift;
		shift += 7n;
	}

	//assert((buf[o] & 0b1000_0000) === 0, 'Malformed varint')

	acc |= BigInt(buf[o++] & 0b0111_1111) << shift;

	return [acc, o - byteOffset];
}

export function bytes(buf, byteOffset = 0) {
	const [len, n] = varint(buf, byteOffset);

	//assert(buf.byteLength - byteOffset - varint.bytes >= len, 'Malformed bytes')
	const b = buf.subarray(byteOffset + n, byteOffset + n + Number(len));

	return [b, n + Number(len)];
}

export function fixed64bit(buf, byteOffset = 0) {
	//assert(buf.byteLength - byteOffset >= 8, 'Malformed 64-bit')
	return [buf.subarray(byteOffset, byteOffset + 8), 8];
}

export function fixed32bit(buf, byteOffset = 0) {
	//assert(buf.byteLength - byteOffset >= 4, 'Malformed 64-bit')
	return [buf.subarray(byteOffset, byteOffset + 4), 4];
}

function _unpack(bytes) {
	var r = [],
		length = bytes.length,
		offset = 0,
		t,
		n;

	while (offset < length) {
		[t, n] = varint(bytes, offset);
		r.push(t);
		offset += n;
	}

	return r;
}

export function unpack(func) {
	return function (bytes) {
		return _unpack(bytes).map(func);
	};
}
